package com.example.myspringframework.beans.factory.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import com.example.myspringframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.example.myspringframework.beans.BeansException;
import com.example.myspringframework.beans.PropertyValue;
import com.example.myspringframework.beans.factory.config.BeanDefinition;
import com.example.myspringframework.beans.factory.config.BeanDefinitionRegistry;
import com.example.myspringframework.beans.factory.config.BeanReference;
import com.example.myspringframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.example.myspringframework.core.io.Resource;
import com.example.myspringframework.core.io.ResourceLoader;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;

/**
 * 解析xml配置文件
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, ResourceLoader resourceLoader) {
        super(beanDefinitionRegistry, resourceLoader);
    }

    /**
     * 从指定位置加载BeanDefinition
     */
    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = this.getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        this.loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
			InputStream inputStream = resource.getInputStream();
			try {
				doLoadBeanDefinitions(inputStream);
			} finally {
				inputStream.close();
			}
		} catch (IOException ex) {
			throw new BeansException("IOException parsing XML document from " + resource, ex);
		}
    }
    protected void doLoadBeanDefinitions(InputStream inputStream){
        Document document = XmlUtil.readXML(inputStream);
		Element root = document.getDocumentElement();

        Element componentScan = XmlUtil.getElement(root, "context:component-scan");
        if (componentScan != null) {
            String scanPath = componentScan.getAttribute("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(scanPath);
        }

        List<Element> childNodes = XmlUtil.getElements(root, "bean");
		for (Element bean:childNodes) {
            //解析bean标签
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");
            String initMethodName = bean.getAttribute("init-method");
            String destroyMethodName = bean.getAttribute("destroy-method");
            String beanScope = bean.getAttribute("scope");
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeansException("Cannot find class [" + className + "]");
            }
            //id优先于name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                //如果id和name都为空，将类名的第一个字母转为小写后作为bean的名称
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethodName);
            beanDefinition.setDestroyMethodName(destroyMethodName);
            if (StrUtil.isNotEmpty(beanScope)) {
                beanDefinition.setScope(beanScope);
            }
            List<Element> elements = XmlUtil.transElements(bean.getChildNodes());
            for (Element element : elements) {
                if ("property".equals(element.getNodeName())) {
                    //解析property标签
                    String nameAttribute = element.getAttribute("name");
                    String valueAttribute = element.getAttribute("value");
                    String refAttribute = element.getAttribute("ref");

                    if (StrUtil.isEmpty(nameAttribute)) {
                        throw new BeansException("The name attribute cannot be null or empty");
                    }

                    Object value = valueAttribute;
                    if (StrUtil.isNotEmpty(refAttribute)) {
                        value = new BeanReference(refAttribute);
                    }
                    PropertyValue propertyValue = new PropertyValue(nameAttribute, value);
                    beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                }
            }
            if (this.getBeanDefinitionRegistry().containsBeanDefinition(beanName)) {
                //beanName不能重名
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            //注册BeanDefinition
            this.getBeanDefinitionRegistry().registerBeanDefinition(beanName, beanDefinition);
		}
    }
    private void scanPackage(String scanPath) {
        String[] basePackages = StrUtil.splitToArray(scanPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.getBeanDefinitionRegistry());
        scanner.doScan(basePackages);
    }
    

}
